﻿///////////////////////////////////////////////////////////////////////////////
//
// wxFormBuilder - A Visual Dialog Editor for wxWidgets.
// Copyright (C) 2005 José Antonio Hurtado
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// Written by
//   José Antonio Hurtado - joseantonio.hurtado@gmail.com
//   Juan Antonio Ortega  - jortegalalmolda@gmail.com
//
///////////////////////////////////////////////////////////////////////////////

// Atencion!!!!!!
// ObjectBase::GetPropertyCount() != ObjectInfo::GetPropertyCount()
//
// En el primer caso devolverá el numero total de propiedades del objeto.
// En el segundo caso sólo devolverá el número de propiedades definidas
// para esa clase.

#ifndef __OBJ__
#define __OBJ__

#include "../utils/wxfbdefs.h"
#include "types.h"
#include <QString> 
#include <QSize>
#include <QPoint>
#include <QBitmap>
#include <list>
#include "../utils/ticpp.h"

///////////////////////////////////////////////////////////////////////////////

class OptionList
{
private:

	std::map< QString, QString > m_options;

public:

	void AddOption( QString option, QString description = QString() )
	{
		m_options[option] = description;
	}
	unsigned int GetOptionCount()
	{
		return (unsigned int)m_options.size();
	}
	const std::map< QString, QString >& GetOptions()
	{
		return m_options;
	}
};

///////////////////////////////////////////////////////////////////////////////

/**
@internal
Data Container for children of a Parent property
*/
class PropertyChild
{
public:
	QString m_name;
	QString m_defaultValue;
	QString m_description;
	PropertyType m_type;
};

///////////////////////////////////////////////////////////////////////////////

class PropertyInfo
{
	friend class Property;

private:
	QString       m_name;
	PropertyType   m_type;
	QString       m_def_value;
	QString       m_min_value;
	QString       m_max_value;
	POptionList  m_opt_list;
	std::list< PropertyChild > m_children; // Only used for parent properties
	QString		m_description;
	QString		m_customEditor; // an optional custom editor for the property grid
    bool            m_isReadOnly;
public:

	PropertyInfo(QString name, PropertyType type, QString def_value, QString min_value,
	             QString max_value, QString description, QString customEditor,
		POptionList opt_list, const std::list< PropertyChild >& children , bool isReadOnly = false);

	~PropertyInfo();

	QString       GetDefaultValue()        { return m_def_value;  }
	QString       GetMinValue()            { return m_min_value;  }
	QString       GetMaxValue()            { return m_max_value;  }
	PropertyType   GetType()                { return m_type;       }
	QString       GetName()                { return m_name;       }
	POptionList    GetOptionList ()         { return m_opt_list;   }
	std::list< PropertyChild >* GetChildren(){ return &m_children; }
	QString		 GetDescription	()		{ return m_description;}
	QString	   GetCustomEditor()		{ return m_customEditor; }
	bool           GetReadOnly()		    { return m_isReadOnly; }
};

class EventInfo
{
private:
	QString m_name;
	QString m_eventClass;
    QString m_defaultValue;
    QString m_description;
public:
  EventInfo(const QString &name,
            const QString &eventClass,
            const QString &defValue,
            const QString &description);

  QString GetName()           { return m_name; }
  QString GetEventClassName() { return m_eventClass; }
  QString GetDefaultValue()   { return m_defaultValue; }
  QString GetDescription()    { return m_description; }
};

///////////////////////////////////////////////////////////////////////////////

class Property
{
private:
	PPropertyInfo m_info;   // pointer to its descriptor
	WPObjectBase  m_object; // pointer to the owner object

	QString m_value;

public:
	Property(PPropertyInfo info, PObjectBase obj = PObjectBase())
	{
		m_object = obj;
		m_info = info;
	}

	PObjectBase GetObject() { return m_object.lock(); }
	QString GetName()                 { return m_info->GetName(); }
	QString GetValue()                { return m_value; }
	void SetValue( QString& val )     { m_value = val; }
    void SetValue( const QChar* val )       { m_value = *val;	}

	PPropertyInfo GetPropertyInfo() { return m_info; }
	PropertyType  GetType()         { return m_info->GetType();  }

	bool IsDefaultValue();
	bool IsNull();
	void SetDefaultValue();
	void ChangeDefaultValue( const QString& value )
	{
		m_info->m_def_value = value;
	}

	////////////////////
    //void SetValue(const wxFontContainer &font);
    void SetFontValue(const QString &font);
    //void SetValue(const wxColour &colour);
	void SetValue(const QString &str, bool format = false);
	void SetValue(const QPoint &point);
	void SetValue(const QSize &size);
	void SetValue(const int integer);
	void SetValue(const double val );

	QString GetValueAsFont();
	QColor GetValueAsColour();
	QPoint  GetValueAsPoint();
	QSize   GetValueAsSize();
	int      GetValueAsInteger();
	QString GetValueAsString();
	QBitmap GetValueAsBitmap();
	QString GetValueAsText();   // sustituye los ('\n',...) por ("\\n",...)

    //wxArrayString GetValueAsArrayString();
	double GetValueAsFloat();
	void SplitParentProperty( std::map< QString, QString >* children );
	QString GetChildFromParent( const QString& childName );
};

class Event
{
private:
  PEventInfo  m_info;   // pointer to its descriptor
  WPObjectBase m_object; // pointer to the owner object
  QString    m_value;  // handler function name
  QString    m_funcBody; // function body
public:
  Event (PEventInfo info, PObjectBase obj)
    : m_info(info), m_object(obj)
  {}
  void SetFuncBody(const QString &value) { m_funcBody = value; }
  QString GetFuncBody() { return m_funcBody; }
  void SetValue(const QString &value) { m_value = value; }
  QString GetValue()                  { return m_value; }
  QString GetName()                   { return m_info->GetName(); }
  PObjectBase GetObject()              { return m_object.lock(); }
  PEventInfo GetEventInfo()            { return m_info; }
};

class PropertyCategory
{
private:
	QString m_name;
	std::vector< QString > m_properties;
	std::vector< QString > m_events;
	std::vector< PPropertyCategory > m_categories;

public:

	PropertyCategory( QString name ) : m_name( name ){}
	void AddProperty( QString name ){ m_properties.push_back( name ); }
	void AddEvent( QString name ){ m_events.push_back( name ); }
	void AddCategory( PPropertyCategory category ){ m_categories.push_back( category ); }
	QString GetName(){ return m_name; }
	QString GetPropertyName( size_t index )
	{
		if ( index < m_properties.size() )
		{
			return m_properties[ index ];
		}
		else
		{
            return "";
		}
	}

	QString GetEventName( size_t index )
	{
		if ( index < m_events.size() )
		{
			return m_events[ index ];
		}
		else
		{
            return "";
		}
	}

	PPropertyCategory GetCategory( size_t index )
	{
		if ( index < m_categories.size() )
		{
			return m_categories[ index ];
		}
		else
		{
			return PPropertyCategory();
		}
	}

	size_t GetPropertyCount() { return m_properties.size(); }
	size_t GetEventCount() { return m_events.size(); }
	size_t GetCategoryCount() { return m_categories.size(); }
};

///////////////////////////////////////////////////////////////////////////////
namespace ticpp
{
	class Document;
	class Element;
}

// The point is to provide an interface for accessing the object's properties
// from the plugin itself, in a safe way.
class IObject
{
 public:
  virtual bool     IsNull (const QString& pname) = 0;
  virtual int      GetPropertyAsInteger (const QString& pname) = 0;
  virtual QString   GetPropertyAsFont    (const QString& pname) = 0;
  virtual QColor GetPropertyAsColour  (const QString& pname) = 0;
  virtual QString GetPropertyAsString  (const QString& pname) = 0;
  virtual QPoint  GetPropertyAsPoint   (const QString& pname) = 0;
  virtual QSize   GetPropertyAsSize    (const QString& pname) = 0;
  virtual QBitmap GetPropertyAsBitmap  (const QString& pname) = 0;
//   virtual wxArrayInt GetPropertyAsArrayInt(const QString& pname) = 0;
//   virtual wxArrayString GetPropertyAsArrayString(const QString& pname) = 0;
  virtual std::vector<std::pair<int, int>> GetPropertyAsVectorIntPair(const QString& pname) = 0;
  virtual double GetPropertyAsFloat(const QString& pname) = 0;
  virtual QString GetChildFromParentProperty( const QString& parentName, const QString& childName ) = 0;
  virtual QString GetClassName() = 0;
  virtual unsigned int  GetChildCount() = 0;
  virtual QString GetObjectTypeName() = 0;
  virtual IObject* GetChildPtr (unsigned int idx) = 0;
  virtual ~IObject(){}
};

class ObjectBase : public IObject, public std::enable_shared_from_this<ObjectBase>
{
	friend class wxFBDataObject;
private:
	QString     m_class;  // class name
	QString     m_type;   // type of object
	WPObjectBase m_parent; // weak pointer, no reference loops please!

	ObjectBaseVector m_children;
	PropertyMap      m_properties;
	EventMap         m_events;
	PObjectInfo      m_info;
	bool m_expanded; // is expanded in the object tree, allows for saving to file
    bool m_isLockEditor;   // is lock editor
protected:
	// utilites for implementing the tree
	static const int INDENT;  // size of indent
	QString GetIndentString(int indent); // obtiene la cadena con el indentado

	ObjectBaseVector& GetChildren()     { return m_children; }
	PropertyMap&      GetProperties()   { return m_properties; }

	// Crea un elemento del objeto
	void SerializeObject( ticpp::Element* serializedElement );

    void SerializeImgs( ticpp::Element* serializedElement );

    void SerializeFonts( ticpp::Element* serializedElement );

	void SerializeCtrlMaps( ticpp::Element* serializedElement );
	// devuelve el puntero "this"
	PObjectBase GetThis() { return shared_from_this(); }

public:

	/// Constructor.
	ObjectBase (QString class_name);

	~ObjectBase() override;

	/**
	Sets whether the object is expanded in the object tree or not.
	*/
	void SetExpanded( bool expanded ){ m_expanded = expanded; }

	/**
	Gets whether the object is expanded in the object tree or not.
	*/
	bool GetExpanded(){ return m_expanded; }
    
	/**
	Gets whether the object is locked in visual editor.
	*/
	bool IsLockEditor(){ return m_isLockEditor; }
	void SetLockEditor( bool isLocked ){ m_isLockEditor = isLocked; }
	/**
	* Obtiene el nombre del objeto.
	*
	* @note No confundir con la propiedad nombre que tienen algunos objetos.
	*       Cada objeto tiene un nombre, el cual será el mismo que el usado
	*       como clave en el registro de descriptores.
	*/
	QString GetClassName() override {
		return m_class;
	}

	/// Gets the parent object
	PObjectBase GetParent () { return m_parent.lock(); }

	PObjectBase GetNonSizerParent ();

	/// Links the object to a parent
	void SetParent(PObjectBase parent)  { m_parent = parent; }

	/**
	* Obtiene la propiedad identificada por el nombre.
	*
	* @note Notar que no existe el método SetProperty, ya que la modificación
	*       se hace a través de la referencia.
	*/
	PProperty GetProperty (QString name);

	PEvent GetEvent(QString name);

	/**
	* Añade una propiedad al objeto.
	*
	* Este método será usado por el registro de descriptores para crear la
	* instancia del objeto.
	* Los objetos siempre se crearán a través del registro de descriptores.
	*/
	void AddProperty (PProperty value);

	void AddEvent(PEvent event);

	/**
	* Obtiene el número de propiedades del objeto.
	*/
	unsigned int GetPropertyCount() { return (unsigned int)m_properties.size(); }

	unsigned int GetEventCount()    { return m_events.size(); }

	/**
	* Obtiene una propiedad del objeto.
	* @todo esta función deberá lanzar una excepción en caso de no encontrarse
	*       dicha propiedad, así se simplifica el código al no tener que hacer
	*       tantas comprobaciones.
	* Por ejemplo, el código sin excepciones sería algo así:
	*
	* @code
	*
	* PProperty plabel = obj->GetProperty("label");
	* PProperty ppos = obj->GetProperty("pos");
	* PProperty psize = obj->GetProperty("size");
	* PProperty pstyle = obj->GetProperty("style");
	*
	* if (plabel && ppos && psize && pstyle)
	* {
	*   wxButton *button = new wxButton(parent,wxID_ANY,
	*    plabel->GetValueAsString(),
	*    ppos->GetValueAsPoint(),
	*    psize->GetValueAsSize(),
	*    pstyle->GetValueAsInteger());
	* }
	* else
	* {
	*   // manejo del error
	* }
	*
	* @endcode
	*
	* y con excepciones:
	*
	* @code
	*
	* try
	* {
	*   wxButton *button = new wxButton(parent,wxID_ANY,
	*     obj->GetProperty("label")->GetValueAsString(),
	*     obj->GetProperty("pos")->GetValueAsPoint(),
	*     obj->GetProperty("size")->GetValueAsSize(),
	*     obj->GetProperty("style")->GetValueAsInteger());
	* }
	* catch (...)
	* {
	*   // manejo del error
	* }
	*
	* @endcode
	*
	*/
	PProperty GetProperty (unsigned int idx); // throws ...;

	PEvent GetEvent (unsigned int idx); // throws ...;

	/**
	* Devuelve el primer antecesor cuyo tipo coincida con el que se pasa
	* como parámetro.
	*
	* Será útil para encontrar el widget padre.
	*/
	PObjectBase FindNearAncestor(QString type);
	PObjectBase FindNearAncestorByBaseClass(QString type);
	PObjectBase FindParentForm();

	/**
	* Obtiene el documento xml del arbol tomando como raíz el nodo actual.
	*/
	void Serialize( ticpp::Document* serializedDocument );

	/**
	* Añade un hijo al objeto.
	* Esta función es virtual, debido a que puede variar el comportamiento
	* según el tipo de objeto.
	*
	* @return true si se añadió el hijo con éxito y false en caso contrario.
	*/
	virtual bool AddChild    (PObjectBase);
	virtual bool AddChild    (unsigned int idx, PObjectBase obj);

	/**
	* Devuelve la posicion del hijo o GetChildCount() en caso de no encontrarlo
	*/
	unsigned int GetChildPosition(PObjectBase obj);
	bool ChangeChildPosition(PObjectBase obj, unsigned int pos);

	/**
	* devuelve la posición entre sus hermanos
	*/
	/*  unsigned int GetPosition();
	bool ChangePosition(unsigned int pos);*/


	/**
	* Elimina un hijo del objeto.
	*/
	void RemoveChild (PObjectBase obj);
	void RemoveChild (unsigned int idx);
	void RemoveAllChildren(){ m_children.clear(); }

	/**
	* Obtiene un hijo del objeto.
	*/
	PObjectBase GetChild (unsigned int idx);

	PObjectBase GetChild (unsigned int idx, const QString& type);

	/**
	* Obtiene el número de hijos del objeto.
	*/
	unsigned int GetChildCount() override {
		return (unsigned int)m_children.size();
	}

	/**
	* Comprueba si el tipo de objeto pasado es válido como hijo del objeto.
	* Esta rutina es importante, ya que define las restricciónes de ubicación.
	*/
	//bool ChildTypeOk (QString type);
	bool ChildTypeOk (PObjectType type);

    bool IsContainer() { return (GetObjectTypeName() == ("container") ); }

	PObjectBase GetLayout();

	/**
	* Devuelve el tipo de objeto.
	*
	* Deberá ser redefinida en cada clase derivada.
	*/
	QString GetObjectTypeName() override {
		return m_type;
	}
	void SetObjectTypeName(QString type) { m_type = type; }

	/**
	* Devuelve el descriptor del objeto.
	*/
	PObjectInfo GetObjectInfo() { return m_info; }
	void SetObjectInfo(PObjectInfo info) { m_info = info; }

	/**
	* Devuelve la profundidad  del objeto en el arbol.
	*/
	int Deep();

	/**
	* Imprime el arbol en un stream.
	*/
	//virtual void PrintOut(ostream &s, int indent);

	/**
	* Sobrecarga del operador inserción.
	*/
	friend std::ostream& operator << (std::ostream &s, PObjectBase obj);

	/////////////////////////
	// Implementación de la interfaz IObject para su uso dentro de los
	// plugins
	bool IsNull(const QString& pname) override;
	int GetPropertyAsInteger(const QString& pname) override;
	QString GetPropertyAsFont(const QString& pname) override;
	QColor GetPropertyAsColour(const QString& pname) override;
	QString GetPropertyAsString(const QString& pname) override;
	QPoint GetPropertyAsPoint(const QString& pname) override;
	QSize GetPropertyAsSize(const QString& pname) override;
	QBitmap GetPropertyAsBitmap(const QString& pname) override;
	double GetPropertyAsFloat(const QString& pname) override;

//	wxArrayInt GetPropertyAsArrayInt(const QString& pname) override;
//	wxArrayString GetPropertyAsArrayString(const QString& pname) override;
	std::vector<std::pair<int, int>> GetPropertyAsVectorIntPair(const QString& pname) override;
	QString GetChildFromParentProperty(const QString& parentName,
	                                    const QString& childName) override;

	IObject* GetChildPtr(unsigned int idx) override {
		return GetChild(idx).get();
	}
};

///////////////////////////////////////////////////////////////////////////////

/**
* Clase que guarda un conjunto de plantillas de código.
*/
class CodeInfo
{
private:
	typedef std::map<QString,QString> TemplateMap;
	TemplateMap m_templates;
public:
	QString GetTemplate(QString name);
	void AddTemplate(QString name, QString _template);
	void Merge( PCodeInfo merger );
};

///////////////////////////////////////////////////////////////////////////////

/**
* Información de objeto o MetaObjeto.
*/
class ObjectInfo
{
public:
	/**
	* Constructor.
	*/
	ObjectInfo(QString class_name, QString name, PObjectType type, WPObjectPackage package, bool startGroup = false );

	virtual ~ObjectInfo() = default;

	PPropertyCategory GetCategory(){ return m_category; }

	unsigned int GetPropertyCount() { return (unsigned int)m_properties.size(); }
	unsigned int GetEventCount()    { return (unsigned int)m_events.size();     }

	/**
	* Obtiene el descriptor de la propiedad.
	*/
	PPropertyInfo GetPropertyInfo(QString name);
	PPropertyInfo GetPropertyInfo(unsigned int idx);

	PEventInfo GetEventInfo(QString name);
	PEventInfo GetEventInfo(unsigned int idx);

	/**
	* Añade un descriptor de propiedad al descriptor de objeto.
	*/
	void AddPropertyInfo(PPropertyInfo pinfo);

	/**
	 * Adds a event descriptor.
	 */
	void AddEventInfo(PEventInfo evtInfo);

	/**
	* Add a default value for an inherited property.
	* @param baseIndex Index of base class returned from AddBaseClass.
	* @param propertyName Property of base class to assign a default value to.
	* @param defaultValue Default value of the inherited property.
	*/
	void AddBaseClassDefaultPropertyValue( size_t baseIndex, const QString& propertyName, const QString& defaultValue );

	/**
	* Get a default value for an inherited property.
	* @param baseIndex Index of base class in the base class vector
	* @param propertName Name of the property to get the default value for
	* @return The default value for the property
	*/
	QString GetBaseClassDefaultPropertyValue( size_t baseIndex, const QString& propertyName );

	/**
	* Devuelve el tipo de objeto, será util para que el constructor de objetos
	* sepa la clase derivada de ObjectBase que ha de crear a partir del
	* descriptor.
	*/
	QString GetObjectTypeName() { return m_type->GetName();   }

	PObjectType GetObjectType() { return m_type; }

	QString GetClassName () { return m_class;  }

	/**
	* Imprime el descriptor en un stream.
	*/
	//virtual void PrintOut(ostream &s, int indent);


	/**
	* Sobrecarga del operador inserción.
	*/
	friend std::ostream& operator << (std::ostream &s, PObjectInfo obj);

	// nos serán utiles para generar el nombre del objeto
	unsigned int GetInstanceCount() { return m_numIns; }
	void IncrementInstanceCount()   { m_numIns++; }
	void ResetInstanceCount() { m_numIns = 0; }

	/**
	* Añade la información de un objeto al conjunto de clases base.
	*/
	size_t AddBaseClass(PObjectInfo base)
	{
		m_base.push_back(base);
		return m_base.size() - 1;
	}

	/**
	* Comprueba si el tipo es derivado del que se pasa como parámetro.
	*/
	bool IsSubclassOf(QString classname);

	PObjectInfo GetBaseClass(unsigned int idx, bool inherited = true);
	void GetBaseClasses(std::vector<PObjectInfo> &classes, bool inherited = true);
	unsigned int GetBaseClassCount(bool inherited = true);

	void SetIconFile(QBitmap icon) { m_icon = icon; }
	QBitmap GetIconFile() { return m_icon; }

	void SetSmallIconFile(QBitmap icon) { m_smallIcon = icon; }
	QBitmap GetSmallIconFile() { return m_smallIcon; }

	void AddCodeInfo(QString lang, PCodeInfo codeinfo);
	PCodeInfo GetCodeInfo(QString lang);

	PObjectPackage GetPackage();

	bool IsStartOfGroup() { return m_startGroup; }

private:
	QString m_class;         // nombre de la clase (tipo de objeto)

	PObjectType m_type;     // tipo del objeto
	WPObjectPackage m_package; 	// Package that the object comes from

	PPropertyCategory m_category;

	QBitmap m_icon;
	QBitmap m_smallIcon; // The icon for the property grid toolbar
	bool m_startGroup; // Place a separator in the palette toolbar just before this widget

	std::map< QString, PCodeInfo > m_codeTemp;  // plantillas de codigo K=language_name T=PCodeInfo

	unsigned int m_numIns;  // número de instancias del objeto

	std::map< QString, PPropertyInfo > m_properties;
	std::map< QString, PEventInfo >    m_events;

	std::vector< PObjectInfo > m_base; // base classes
	std::map< size_t, std::map< QString, QString > > m_baseClassDefaultPropertyValues;
	// designer
};


/**
* Information for lcd template.
*/
class LcdTemplateInfo
{
public:
	/**
	* Constructor.
	*/
	LcdTemplateInfo(QString class_name, QString name, PObjectType type);

	virtual ~LcdTemplateInfo() = default;


	unsigned int GetPropertyCount() { return (unsigned int)m_properties.size(); }
	/**
	* Obtiene el descriptor de la propiedad.
	*/
	PPropertyInfo GetPropertyInfo(QString name);
	PPropertyInfo GetPropertyInfo(unsigned int idx);

	/**
	* Añade un descriptor de propiedad al descriptor de objeto.
	*/
	void AddPropertyInfo(PPropertyInfo pinfo);

	/**
	* Devuelve el tipo de objeto, será util para que el constructor de objetos
	* sepa la clase derivada de ObjectBase que ha de crear a partir del
	* descriptor.
	*/
	QString GetObjectTypeName() { return m_type->GetName();   }

	PObjectType GetObjectType() { return m_type; }

	QString GetClassName () { return m_class;  }

	QString GetObjectName () { return m_name;  }

private:
	QString m_class;         // nombre de la clase (tipo de objeto)
    QString m_name;         // object name
	PObjectType m_type;     // tipo del objeto
	std::map< QString, PPropertyInfo > m_properties;
};

#endif
